package com.xiye.system.config;

import com.xiye.common.entity.User;
import com.xiye.common.session.redis.impl.RedisManager;
import com.xiye.common.util.Const;
import com.xiye.common.util.IpUtil;
import com.xiye.common.util.Jurisdiction;
import net.sf.json.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author 西野
 * @version V1.0
 * @title HttpAspect.java
 * @package com.xiye.system.config
 * @description 请求拦截，输出日志
 * @date 2018-08-14
 */
@Aspect
@Component
public class HttpAspect {

    @Autowired
    private RedisManager redisManager;

    private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut(value = "execution (public * com.xiye.*.controller..*(..))")
    public void log() {
    }

    @Before(value = "log()")
    public void doBefore() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String path = request.getServletPath();
        /* 日志中的用户名是通过session中保存的用户信息获取，但是退出登录后，session被销毁，所以在退出请求执行前先手动传递一个用户名 */
        if (path.equals("/login/logout")) {
            User user = Jurisdiction.getUser(Const.SESSION_USER); // 读取session中的用户信息(单独用户信息)
            /* 请求时间戳 */
            request.setAttribute("USERNAME", user.getUSERNAME());
        }
        /* 为每个请求添加时间戳 */
        request.setAttribute("timestamp", System.currentTimeMillis());
        logger.info("Request Address : 【" + request.getServletPath() + "】");
    }

    @After(value = "log()")
    public void doAfter(JoinPoint joinPoint) throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        long beginTime = (long) request.getAttribute("timestamp");
        Map<String, String> map = new HashMap<String, String>();
        User user = Jurisdiction.getUser(Const.SESSION_USER); // 读取session中的用户信息(单独用户信息)
        String username = user == null ? request.getAttribute("USERNAME").toString() : user.getUSERNAME();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        // 获取目标类名
        String targetName = joinPoint.getTarget().getClass().getName();
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        map.put("methodName", (targetName + "." + methodName));
        // 获取相关参数
        Object[] arguments = joinPoint.getArgs();
        // 生成类对象
        Class targetClass = Class.forName(targetName);
        // 获取该类中的方法
        Method[] methods = targetClass.getMethods();
        // 时间
        map.put("time", df.format(new Date()));
        // 日志请求url
        map.put("RequestUrl", request.getRequestURI());
        // 请求方式
        map.put("Method", request.getMethod());
        // 请求用户
        map.put("USERNAME", username);
        // 请求IP
        map.put("ip", request.getMethod());
        // IP地址
        map.put("ipaddress", IpUtil.getIpCity(IpUtil.getIpAddr(request)));
        //请求结束时间
        long endTime = System.currentTimeMillis();
        // 请求耗时（单位毫秒）
        Long logElapsedTime = endTime - beginTime;
        map.put("CostTime", String.valueOf(logElapsedTime.intValue()));
        List list = new ArrayList();
        /* 判断redis中是否存在
         * 如果没有则直接存
         * 如果因为Elasticsearch服务down掉导致没有清空redis中的日志，则先读取日志list并添加，以便于Elasticsearch服务恢复后直接读取
         * Elasticsearch从redis读取数据后会删除redis中的key
         */
        if (redisManager.getList("logstash:redis") == null) {
            list.add(JSONObject.fromObject(map).toString());
            redisManager.addList("logstash:redis", list);
        } else {
            list = redisManager.getList("logstash:redis");
            list.add(JSONObject.fromObject(map).toString());
            redisManager.addList("logstash:redis", list);
        }

        logger.info("Request End : 【" + request.getServletPath() + "】");
    }

}
